package com.ljc.db.backend.dm.dataItem;

import com.ljc.db.backend.common.SubArray;
import com.ljc.db.backend.dm.DataManagerImpl;
import com.ljc.db.backend.dm.page.Page;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * dataItem 结构如下：
 * [ValidFlag] [DataSize] [Data]
 * ValidFlag 1字节，0为合法，1为非法 【逻辑删除】
 * DataSize  2字节，标识Data的长度
 */
public class DataItemImpl implements DataItem {

    static final int OF_VALID = 0;  // 逻辑删除表示符，起始位置为 0
    static final int OF_SIZE = 1;   // 记录数据长度，起始位置为 1
    static final int OF_DATA = 3;   // 数据，起始位置为 3

    private SubArray raw;
    private byte[] oldRaw;
    private Lock rLock;
    private Lock wLock;
    private DataManagerImpl dm; // DM方便缓存 DataItem 并且在修改数据时记录日志
    private long uid;           // 数据地址【页号+偏移量】转换为long型的uid，高32位存放页号，低32位存放偏移量
    private Page pg;

    public DataItemImpl(SubArray raw, byte[] oldRaw, Page pg, long uid, DataManagerImpl dm) {
        this.raw = raw;
        this.oldRaw = oldRaw;
        ReadWriteLock lock = new ReentrantReadWriteLock();
        rLock = lock.readLock();
        wLock = lock.writeLock();
        this.dm = dm;
        this.uid = uid;
        this.pg = pg;
    }

    public boolean isValid() {
        return raw.raw[raw.start + OF_VALID] == (byte)0;
    }

    /**
     * 该方法返回的数组是数据共享的，而不是拷贝实现的，所以使用了 SubArray
     */
    @Override
    public SubArray data() {
        return new SubArray(raw.raw, raw.start + OF_DATA, raw.end);
    }

    // ------ 读写锁保证对 DataItem 的操作是原子性的
    /**
     * 准备对DataItem进行修改
     */
    @Override
    public void before() {
        wLock.lock();
        pg.setDirty(true);
        // 将新数据覆盖旧数据
        System.arraycopy(raw.raw, raw.start, oldRaw, 0, oldRaw.length);
    }

    /**
     * 撤销DataItem的修改
     */
    @Override
    public void unBefore() {
        System.arraycopy(oldRaw, 0, raw.raw, raw.start, oldRaw.length);
        wLock.unlock();
    }

    /**
     * 在完成对DataItem的修改后对本次事务在.log文件中进行记录
     */
    @Override
    public void after(long xid) {
        dm.logDataItem(xid, this);
        wLock.unlock();
    }

    @Override
    public void release() {
        dm.releaseDataItem(this);
    }

    @Override
    public void lock() {
        wLock.lock();
    }

    @Override
    public void unlock() {
        wLock.unlock();
    }

    @Override
    public void rLock() {
        rLock.lock();
    }

    @Override
    public void rUnLock() {
        rLock.unlock();
    }

    @Override
    public Page page() {
        return pg;
    }

    @Override
    public long getUid() {
        return uid;
    }

    @Override
    public byte[] getOldRaw() {
        return oldRaw;
    }

    @Override
    public SubArray getRaw() {
        return raw;
    }
    
}
